import { uuid } from '@/utils/uuid'
import { debounce } from 'lodash-es'

export type WatermarkStyle = Partial<{
  width: number
  height: number
  fontSize: number
  fontFamily: string
  fontWeight: string
  color: string
  textAlign: string
  textBaseline: string
  alpha: number
}>

type CanvasStyle = {
  width: number
  height: number
  font: string
  fillStyle: string
  textAlign: string
  textBaseline: string
  globalAlpha: number
}

// 默认的画布样式
const defaultCanvasStyle: WatermarkStyle = {
  width: 200,
  height: 140,
  fontSize: 12,
  fontFamily: 'Reggae One',
  fontWeight: 'normal',
  color: 'rgba(0, 0, 0, 0.2)',
  textAlign: 'left',
  textBaseline: 'middle',
  alpha: 1
}

// 盲水印默认样式
const defaultBlindCanvasStyle: WatermarkStyle = {
  width: 200,
  height: 40,
  fontSize: 16,
  fontFamily: 'Reggae One',
  fontWeight: 'normal',
  color: 'red',
  textAlign: 'center',
  textBaseline: 'middle',
  alpha: 0.015
}

/**
 * @description 使用水印的hooks
 * @param string  水印盒子的父盒子标签索引
 * @param boolean  是否启动盲水印，默认开启
 * @param boolean 是否开启对水印的恶意删除与修改拦截，默认开启
 */
export function useWatermark(
  appendEl: string,
  blind = true,
  preventDeletion = true
) {
  // 承载水印的div盒子
  const watermarkEl = shallowRef<HTMLElement | null>(null)
  const watermarkId = `proWatermark-${uuid()}`

  let clearCallBack: ((() => void) | undefined)[] = []

  // 1. 得到文字背景base64图
  const createBase64 = (content: string, watermarkStyle?: WatermarkStyle) => {
    const {
      width,
      height,
      fontWeight,
      fontSize,
      fontFamily,
      color,
      alpha,
      ...otherStyle
    } = {
      ...(blind ? defaultBlindCanvasStyle : defaultCanvasStyle),
      ...(watermarkStyle || {})
    } as Required<WatermarkStyle>
    const font = `${fontWeight} ${fontSize}px ${fontFamily}`
    const style: CanvasStyle = {
      ...otherStyle,
      width,
      height,
      font,
      fillStyle: color,
      globalAlpha: alpha
    }

    return blind
      ? createBlindBase64(content, style)
      : createBrightBase64(content, style)
  }

  // 得到文字背景base64图--明水印
  const createBrightBase64 = (content: string, canvasStyle: CanvasStyle) => {
    const { width, height, ...otherStyle } = canvasStyle
    const can = document.createElement('canvas')
    Object.assign(can, {
      width,
      height
    })

    const cans = can.getContext('2d')
    if (!cans) return
    cans.rotate((-20 * Math.PI) / 120)
    cans.translate(0, 0)
    Object.assign(cans, { ...otherStyle })
    cans.fillText(content, width / 20 - 20, height / 2)
    return can.toDataURL('image/png')
  }

  //  得到文字背景base64图--盲水印
  const createBlindBase64 = (content: string, canvasStyle: CanvasStyle) => {
    const can = document.createElement('canvas')
    const { width, height, ...otherStyle } = {
      ...defaultBlindCanvasStyle,
      ...(canvasStyle || {})
    } as Required<WatermarkStyle>
    Object.assign(can, { width, height })

    const cans = can.getContext('2d')
    if (!cans) return
    cans.globalAlpha = 0.015
    cans.translate(0, 0)
    Object.assign(cans, { ...otherStyle })
    cans.fillText(content, width / 2, height / 2)
    return can.toDataURL('image/png')
  }

  // 2. 绘制水印层
  const createWatermark = (
    content: string,
    watermarkStyle?: WatermarkStyle
  ) => {
    nextTick(() => {
      if (watermarkEl.value) {
        return watermarkId
      }
      const divEl = document.createElement('div')
      divEl.id = watermarkId
      divEl.setAttribute(
        'style',
        'pointer-events: none;position:absolute;top:0;left:0;zIndex:100000'
      )
      const parentEl = document.querySelector(appendEl)
      if (!parentEl) return watermarkId
      parentEl.style.position = 'relative'
      watermarkEl.value = divEl
      const { clientHeight: height, clientWidth: width } = parentEl
      updateWatermark({
        content,
        width: width,
        height: height,
        watermarkStyle
      })
      parentEl.appendChild(divEl)
      return watermarkId
    })
  }

  // 3. 更新水印（用于窗口大小发生改变场景
  const updateWatermark = (options: {
    content: string
    watermarkStyle?: WatermarkStyle
    width: number
    height: number
  }) => {
    const el = watermarkEl.value
    if (!el) return
    el.style.width = `${options.width}px`
    el.style.height = `${options.height}px`
    el.style.background = `url(${createBase64(
      options.content,
      options.watermarkStyle
    )}) left top repeat`
  }

  // 监听窗口大小变化更新
  const initScreenEventListener = (
    content,
    watermarkStyle?: WatermarkStyle
  ): (() => void) | undefined => {
    const targetNode = document.querySelector(appendEl)
    if (!targetNode) return

    // 当观察到变动时执行的回调函数
    const callback = debounce((entries) => {
      entries.forEach((entry) => {
        updateWatermark({
          content,
          watermarkStyle,
          width: entry.contentRect.width,
          height: entry.contentRect.height
        })
      })
    }, 200)

    let screenObserver: ResizeObserver | null = new ResizeObserver((entries) =>
      callback(entries)
    )

    screenObserver.observe(targetNode)

    return () => {
      screenObserver?.disconnect()
      screenObserver = null
    }
  }

  // 4. 对外提供的设置水印方法
  const setWatermark = (content: string, watermarkStyle?: WatermarkStyle) => {
    createWatermark(content, watermarkStyle)

    clearCallBack.push(
      initScreenEventListener(content, watermarkStyle),
      ...(preventDeletion
        ? [initParentNodeObserver(), initWatermarkObserver()]
        : [])
    )
  }

  // 5. 清除水印
  const clearWatermark = () => {
    try {
      clearCallBack.forEach((callBack) => {
        callBack && callBack()
      })
      clearCallBack = []

      const parentNode = appendEl && document.querySelector(appendEl)
      if (parentNode && watermarkEl.value) {
        parentNode.removeChild(watermarkEl.value as Node)
      }
    } catch (err) {
      console.log(err)
    } finally {
      watermarkEl.value = null
    }
  }

  // 6. 得到水印盒子id
  const getWatermarkElID = (): string => {
    return watermarkId
  }

  // 7. 防止水印盒子被删除(父盒子监听子元素是否被删除了)
  const initParentNodeObserver = (): (() => void) | undefined => {
    const parentNode = appendEl && document.querySelector(appendEl)
    if (!parentNode) return

    const parentNodeObserverCallback = (mutationsList) => {
      mutationsList.forEach((mutation) => {
        switch (mutation.type) {
          case 'childList':
            if (
              mutation.removedNodes.length > 0 &&
              mutation.removedNodes[0].id === watermarkId
            ) {
              parentNode.appendChild(mutation.removedNodes[0])
            }
            break
          default:
            break
        }
      })
    }
    let parentNodeObserver: MutationObserver | null = new MutationObserver(
      parentNodeObserverCallback
    )
    parentNodeObserver.observe(parentNode, {
      childList: true
    })
    return () => {
      parentNodeObserver?.disconnect()
      parentNodeObserver = null
    }
  }

  // 8.防止水印盒子被修改
  const initWatermarkObserver = (): (() => void) | undefined => {
    if (!watermarkEl.value) return

    const options = {
      attributes: true,
      attributeOldValue: true,
      characterData: true,
      characterDataOldValue: true
    }
    const watermarkObserverCallback = (mutationsList) => {
      const targetNode = watermarkEl.value as HTMLElement
      mutationsList.forEach((mutation) => {
        switch (mutation.type) {
          case 'attributes':
            watermarkObserver?.disconnect()
            targetNode.setAttribute(mutation.attributeName, mutation.oldValue)
            {
              const parentNode = document.querySelector(appendEl)
              if (parentNode) {
                const { clientHeight: height, clientWidth: width } = parentNode
                targetNode.style.height = `${height}px`
                targetNode.style.width = `${width}px`
              }
            }
            setTimeout(() => {
              watermarkObserver?.observe(watermarkEl.value as Node, options)
            })
            break
          default:
            break
        }
      })
    }

    let watermarkObserver: MutationObserver | null = new MutationObserver(
      watermarkObserverCallback
    )

    watermarkObserver.observe(watermarkEl.value as Node, options)

    return () => {
      watermarkObserver?.disconnect()
      watermarkObserver = null
    }
  }

  return { setWatermark, clearWatermark, getWatermarkElID }
}
